﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using DynaPrecision.LinearSystem;
namespace DynaPrecision.DataFitting
{
	public class L2NormFittedFunction<Tx, Ty> : FunctionAnalysis.IFunction<Tx, Ty>
	{
		public IEnumerable<Func<Tx, Ty>> BaseFunctions
		{
			get
			{
				return _storage.Keys;
			}
		}
		public IEnumerable<Ty> Coefficients
		{
			get
			{
				return _storage.Values;
			}
		}
		public void Add(Func<Tx, Ty> aFunc)
		{
			_storage.Add(aFunc, DefaultValueProvider<Ty>.Zero);
		}
		public void AddRange(IEnumerable<Func<Tx, Ty>> Func)
		{
			foreach (var f in Func)
			{
				this.Add(f);
			}
		}
		public void AddRange(params Func<Tx, Ty>[] Func)
		{
			foreach (var f in Func)
			{
				this.Add(f);
			}
		}
		private Dictionary<Func<Tx, Ty>, Ty> _storage;

		public L2NormFittedFunction()
		{
			_storage = new Dictionary<Func<Tx, Ty>, Ty>();
		}
		public void Fit(IEnumerable<Tx> xs, IEnumerable<Ty> ys)
		{
			Fit<LinearSystem.DoolittleDecompositionSolver>(xs, ys);
		}
		public void Fit<TLinearSystemSolver>(IEnumerable<Tx> xs, IEnumerable<Ty> ys)
			where TLinearSystemSolver : LinearSystem.ILinearSystemSolver, new()
		{
			var a = new L2NormGeneratedMatrix<Tx, Ty>();
			a.FunctionList = this.BaseFunctions.ToList();
			a.Xs.AddRange(xs);

			var b = new L2YMatrix<Tx, Ty>();
			b.FunctionList = this.BaseFunctions.ToList();
			b.AddXY(xs, ys);

			var currentC = (new TLinearSystemSolver()).SolveFor<LinearSystem.MultiDimensionalArray<Ty>, Ty>(a, b);

			for (int i = 0; i < currentC.GetLength(0); i++)
			{
				_storage[_storage.ToList()[i].Key] = currentC[i,0];
			}
		}



		#region IFunction<Tx,Ty> Members

		public Func<Tx, Ty> Functor
		{
			get
			{
				return x => _storage.Aggregate(DefaultValueProvider<Ty>.Zero, (_x, _x2) => (_x + (dynamic)_x2.Key(x) * _x2.Value));
			}
		}

		public FunctionAnalysis.IFunction<Tx, Ty> Derivative
		{
			get { throw new NotImplementedException(); }
		}

		public FunctionAnalysis.IFunction<Tx, Ty> Integral
		{
			get { throw new NotImplementedException(); }
		}

		#endregion
	}
}
